/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       saved_combat.h

	$Header: /game/saved_combat.h $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "saved_combat.h"

#include "adventure_ai.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_map_window.h"
#include "army.h"
#include "battlefield.h"
#include "battlefield_terrain_map.h"
#include "combat_context_adv_object.h"
#include "combat_context_army.h"
#include "combat_window.h"
#include "creature_array.h"
#include "ownable_garrisonable_adv_object.h"
#include "player.h"
#include "run_combat.h"
#include "town.h"

// ----------------------------------------------------------------------------------
// saved information about a combat
// ----------------------------------------------------------------------------------
t_saved_combat::t_saved_combat()
{
}

// ----------------------------------------------------------------------------------
// saved information about a combat
// ----------------------------------------------------------------------------------
t_saved_combat::t_saved_combat( t_combat_window& window )
{
	m_battlefield = window.get_battlefield();
	m_context = window.get_context();
	m_terrain_map = window.get_terrain_map();
}

t_combat_context* t_saved_combat::get_context() const
{
	return m_context;
}

// ----------------------------------------------------------------------------------
// saved information about a combat
// ----------------------------------------------------------------------------------
static int const k_current_version = 0;

bool t_saved_combat::read( std::streambuf& stream, t_adventure_map* adventure_map )
{
	int version = get<t_uint16>( stream );

	if (version < 0 || version > k_current_version)
		return false;

	t_combat_context_type	context_type;

	context_type = t_combat_context_type( get<t_int8>( stream ) );
	switch (context_type)
	{
		case k_combat_context_type_adv_object:
			m_context = new t_combat_context_adv_object;
			break;

		case k_combat_context_type_army:
			m_context = new t_combat_context_army;
			break;

		case k_combat_context_type_script:
			return false;

		default:
			return false;
	}
	if (!m_context->read( stream, *adventure_map ))
		return false;
	m_terrain_map = new t_battlefield_terrain_map;
	if (!m_terrain_map->read( stream ))
		return false;
	m_battlefield = new t_battlefield( m_context->get_attacker(), m_context->get_defender() );
	return m_battlefield->read( stream );
}

// ----------------------------------------------------------------------------------
// saved information about a combat
// ----------------------------------------------------------------------------------
bool t_saved_combat::write( std::streambuf& stream ) const
{
	put<t_uint16>( stream, k_current_version );

	put<t_uint8>( stream, m_context->get_type() );
	if (!m_context->write( stream ))
		return false;
	if (!m_terrain_map->write( stream ))
		return false;
	return m_battlefield->write( stream );
}

// ----------------------------------------------------------------------------------
// saved information about a combat
// ----------------------------------------------------------------------------------
void t_saved_combat::launch( t_adventure_map* adventure_map, t_adventure_frame* adventure_frame )
{
	t_counted_ptr<t_combat_window>  combat_window;
	t_adventure_map_window*			window;
	t_army_ptr						new_army_ptr;

	t_adventure_object_ptr			defender_ptr;
	t_adventure_object_ptr			attacker_ptr;

	t_creature_array* array = m_context->get_defender();
	if (array->get_adventure_object() != NULL)
		defender_ptr = array->get_adventure_object();
	if (array->get_adventure_object() != NULL)
		attacker_ptr = array->get_adventure_object();

	m_context->get_attacker()->set_in_combat( true );
	m_context->get_defender()->set_in_combat( true );
	window = adventure_map->get_map_window();
	window->center_view( m_context->get_location() );
	combat_window = new t_combat_window( *this, 0 );
	adventure_map->clear_saved_combat();
	combat_window->start();
	combat_window->run_modal();
	new_army_ptr = combat_window->get_new_army_ptr();

	t_combat_result		result = combat_window->get_result();
	t_combat_context&	combat_context = *combat_window->get_context();

	on_combat_end( combat_context, result, new_army_ptr );
	if (!adventure_map->is_game_over())
		adventure_map->resume_turn( adventure_frame );
}

// ----------------------------------------------------------------------------------
// saved information about a combat
// ----------------------------------------------------------------------------------
t_battlefield_terrain_map* t_saved_combat::get_terrain_map()
{
	return m_terrain_map;
}

t_combat_context::t_combat_context( t_creature_array*							attacker, 
									t_creature_array*							defender,
									t_adv_map_point const&						location,
									t_town*										town,
									t_ownable_garrisonable_adv_object const*	garrison )
{
	m_attacker = attacker;
	m_defender = defender;
	m_garrison = garrison;
	m_location = location;
	m_town = town;
}

// ----------------------------------------------------------------------------------
// abstract context for combat
// ----------------------------------------------------------------------------------
t_combat_context::~t_combat_context()
{
}

t_adventure_map* t_combat_context::get_adventure_map() const
{
	t_adventure_map* result = m_attacker->get_map();

	if (result == 0)
		result = m_defender->get_map();
	return result;
}		

t_adventure_frame* t_combat_context::get_adventure_frame() const
{
	t_adventure_frame* result = m_attacker->get_adventure_frame();

	if (result == 0)
		result = m_defender->get_adventure_frame();
	return result;
}

bool const * t_combat_context::get_are_real_armies() const
{
	static bool const k_default_are_real[2] = { true, true };

	return k_default_are_real;
}

static int const k_context_version = 1;

bool t_combat_context::base_read( std::streambuf& stream, t_adventure_map& adventure_map,
								  int version )
{
	t_adventure_object*			object;
	t_adventure_global_id		id;

	id = read_adventure_global_id( stream );
	object = adventure_map.get_object_by_global_id( id );
	m_garrison = dynamic_cast< t_ownable_garrisonable_adv_object* >( object );
	id = read_adventure_global_id( stream );
	object = adventure_map.get_object_by_global_id( id );
	m_town = dynamic_cast<t_town*>( object );
	if (version < 1)
		m_was_cursor_visible = true;
	else
		m_was_cursor_visible = (get<t_uint8>( stream ) != 0);
	::read( stream, &m_location, sizeof( m_location ));
	return true;
}

bool t_combat_context::base_read( std::streambuf& stream, t_adventure_map& adventure_map )
{
	int version = get<t_uint16>( stream );

	if (version < 0 || version > k_context_version)
		return false;
	return base_read( stream, adventure_map, version );
}

bool t_combat_context::save_allowed() const
{
	return false;
}

bool t_combat_context::write( std::streambuf& stream ) const
{
	t_adventure_global_id	id;
	
	put<t_uint16>( stream, k_context_version );
	if (m_garrison == 0)
		id = k_invalid_global_id;
	else
		id = m_garrison->get_global_id();
	write_adventure_global_id( stream, id );
	if (m_town == 0)
		id = k_invalid_global_id;
	else
		id = m_town->get_global_id();
	write_adventure_global_id( stream, id );
	put<t_uint8>( stream, m_was_cursor_visible );
	::write( stream, &m_location, sizeof( m_location ));
	return true;
}

// ----------------------------------------------------------------------------------
// context for a saved combat that involves an adventure object
// ----------------------------------------------------------------------------------
t_combat_context_adv_object::t_combat_context_adv_object()
{
}

t_combat_context_adv_object::t_combat_context_adv_object( t_army*						attacker, 
											t_creature_array*							defender,
											t_adv_map_point const&						location,
											t_town*										town,
											t_ownable_garrisonable_adv_object const*	garrison )
						   : t_combat_context( attacker, defender, location, town, garrison )
{
	m_army = attacker;
}

t_combat_context_type t_combat_context_adv_object::get_type() const
{
	return k_combat_context_type_adv_object;
}

// ----------------------------------------------------------------------------------
// context for a saved combat that involves an adventure object
// ----------------------------------------------------------------------------------
void t_combat_context_adv_object::on_combat_end( t_combat_result result )
{
	t_adventure_object* object = m_defender->get_adventure_object();
	t_adventure_frame*	frame = m_defender->get_adventure_frame();

	object->on_combat_end( m_army, result, m_losses, frame );
	get_adventure_map()->post_trigger( m_army, object, frame );
}

// ----------------------------------------------------------------------------------
// context for a saved combat that involves an adventure object
// ----------------------------------------------------------------------------------
static int const k_current_saved_context_version = 1;
bool t_combat_context_adv_object::read( std::streambuf& stream, 
											  t_adventure_map& adventure_map )
{
	t_adventure_object*		object;
	t_adventure_global_id	id;
	int						version = get<t_uint16>( stream );

	if (version < 0 || version > k_current_saved_context_version)
		return false;

	if (version < 1)
	{
		if (!base_read( stream, adventure_map, 0 ))
			return false;
	}
	else if (!base_read( stream, adventure_map ))
		return false;

	id = read_adventure_global_id( stream );
	object = adventure_map.get_object_by_global_id( id );
	m_army = dynamic_cast<t_army*>( object );
	m_attacker = m_army;
	id = read_adventure_global_id( stream );
	object = adventure_map.get_object_by_global_id( id );
	m_defender = dynamic_cast<t_creature_array*>( object );
	return true;
}

// ----------------------------------------------------------------------------------
// context for a saved combat that involves an adventure object
// ----------------------------------------------------------------------------------
bool t_combat_context_adv_object::write( std::streambuf& stream ) const
{
	t_adventure_global_id	id;
	
	put<t_uint16>( stream, k_current_saved_context_version );
	if (!t_combat_context::write( stream ))
		return false;
	id = m_army->get_adventure_object()->get_global_id();
	write_adventure_global_id( stream, id );
	assert( m_attacker == m_army );
	id = m_defender->get_adventure_object()->get_global_id();
	write_adventure_global_id( stream, id );
	return true;
}

bool t_combat_context_adv_object::save_allowed() const
{
	return true;
}
